package network.t_02_nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * https://www.cnblogs.com/jhxxb/p/11303947.html
 * 使用 NIO 完成网络通信的三个核心：
 * 1. 通道（Channel）：负责连接
 * java.nio.channels.Channel 接口：
 * |--SelectableChannel
 * |--SocketChannel
 * |--ServerSocketChannel
 * |--DatagramChannel
 * |--Pipe.SinkChannel
 * |--Pipe.SourceChannel
 * 2. 缓冲区（Buffer）：负责数据的存取
 * 3. 选择器（Selector）：是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
 * 可以监听的事件类型（可使用 SelectionKey 的四个常量表示）
 * * 读: SelectionKey.OP_READ （1）
 * * 写: SelectionKey.OP_WRITE （4）
 * * 连接: SelectionKey.OP_CONNECT （8）
 * * 接收: SelectionKey.OP_ACCEPT （16）
 * <p>
 * Selector 常用方法
 * * Set<SelectionKey> keys()：所有的 SelectionKey 集合。代表注册在该 Selector上的 Channel
 * * selectedKeys()：被选择的 SelectionKey 集合。返回此Selector的已选择键集
 * * intselect()：监控所有注册的 Channel，当它们中间有需要处理的 IO 操作时，该方法返回，并将对应得的 SelectionKey 加入被选择的 SelectionKey 集合中，该方法返回这些 Channel 的数量。
 * * int select(long timeout)：可以设置超时时长的 select() 操作
 * * int selectNow()：执行一个立即返回的 select() 操作，该方法不会阻塞线程
 * * Selector wakeup()：使一个还未返回的 select() 方法立即返回
 * * void close()：关闭该选择器
 */
public class NIOServer {
    static ExecutorService threadPool = Executors.newFixedThreadPool(8);

    public static void main(String[] args) throws IOException {
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            serverSocketChannel.socket().bind(new InetSocketAddress(6666));
            serverSocketChannel.configureBlocking(false); // 设置非阻塞

            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册关心的事件
            while (true) {
                selector.select();
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (!key.isValid()) continue;
                    if (key.isAcceptable()) { // 新客户端连接
                        SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) { // 户端有数据
                        handler(key);
                        // threadPool.execute(() -> handler(key));
                    }
                }
            }
        }
    }

    static void handler(SelectionKey key) {
        try (SocketChannel socketChannel = (SocketChannel) key.channel()) {
            ByteBuffer buffer = ByteBuffer.allocate(5);
            while (buffer.hasRemaining()) socketChannel.read(buffer);
            buffer.flip();
            byte t = buffer.get();
            buffer = ByteBuffer.allocate(buffer.getInt());
            while (buffer.hasRemaining()) socketChannel.read(buffer);
            buffer.flip();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            System.out.println(new String(bytes, StandardCharsets.UTF_8) + "\n收到数据");

            buffer = ByteBuffer.allocate(5 + bytes.length);
            buffer.put(t);
            buffer.putInt(bytes.length);
            buffer.put(bytes);
            buffer.flip();
            while (buffer.hasRemaining()) socketChannel.write(buffer);
            System.out.println("发送完毕");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}